// 当文件增多 极有可能出现头文件重复包含 使用预处理指令
// 使得当该头文件已被包含时 不再重复包含该文件
#ifndef __MY_FILEUTIL__
#define __MY_FILEUTIL__

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <experimental/filesystem>
#include <sys/stat.h>
#include "./mylib/include/bundle.h"
#include "log.hpp"

// 同一个工程中允许存在多个相同名称的命名空间 最后合成同一个命名空间
namespace cloudBackup
{
	// 简洁命名
	namespace filesystem = std::experimental::filesystem;

	class FileUtil
	{
	private:
		std::string _filePath; // ./test.txt /home/lhr/linux/cloud_backup/a/b/c/test.txt

	public:
		FileUtil(const std::string &filePath)
			: _filePath(filePath)
		{
		}

		// 获取文件大小
		int64_t FileSize()
		{
			struct stat statBuf;
			// int stat(const char *file, stat *buf)
			if (stat(_filePath.c_str(), &statBuf) < 0)
			{
				Log::log(LOGERROR, "FileUtil::stat-get file size failed!: %s: %d", strerror(errno), errno);
				return -1;
			}
			return statBuf.st_size;
		}

		// 由文件路径获取纯文件名称
		std::string FileName()
		{
			// ./a/b/c/test.txt --> test.txt
			size_t pos = _filePath.find_last_of("/");
			if (pos == std::string::npos) // 没找到/ 说明本身就是不带路径的文件
				return _filePath;

			return _filePath.substr(pos + 1);
			// return filesystem::path(_filename).filename().string(); //跨平台
		}

		// 获取最后一次访问时间
		time_t LastATime()
		{
			struct stat statBuf;
			// int stat(const char *file, stat *buf)
			if (stat(_filePath.c_str(), &statBuf) < 0)
			{
				Log::log(LOGERROR, "FileUtil::stat-get last_atime failed!: %s: %d", strerror(errno), errno);
				return -1;
			}
			return statBuf.st_atime;
		}

		// 获取最后一次状态改变时间
		time_t LastCTime()
		{
			struct stat statBuf;
			// int stat(const char *file, stat *buf)
			if (stat(_filePath.c_str(), &statBuf) < 0)
			{
				Log::log(LOGERROR, "FileUtil::stat-get last_ctime failed!: %s: %d", strerror(errno), errno);
				return -1;
			}
			return statBuf.st_ctime;
		}

		// 获取最后一次修改时间
		time_t LastMTime()
		{
			struct stat statBuf;
			// int stat(const char *file, stat *buf)
			if (stat(_filePath.c_str(), &statBuf) < 0)
			{
				Log::log(LOGERROR, "FileUtil::stat-get last_mtime failed!: %s: %d", strerror(errno), errno);
				return -1;
			}
			return statBuf.st_mtime;
		}

		// 获取文件pos后len长度的数据 传递给content
		bool GetPosLen(std::string *content, size_t pos, size_t len)
		{
			// pos后文件长度不足len
			size_t fsize = this->FileSize();
			if (pos + len > fsize)
			{
				Log::log(LOGERROR, "FileUtil::GetPosLen pos + len > fsize !: %s: %d", strerror(errno), errno);
				return false;
			}

			std::ifstream ifs;
			ifs.open(_filePath, std::ios::binary);
			if (ifs.is_open() == false)
			{
				Log::log(LOGERROR, "FileUtil::GetPosLen::ifs.is_open() failed !: %s: %d", strerror(errno), errno);
				return false;
			}

			ifs.seekg(pos, std::ios::beg);
			content->resize(len);
			// &(*content)[0] == &( (*content)[0] ) 这么做是因为read接收char*类型
			ifs.read(&(*content)[0], len);
			// 检查在读取文件内容的过程中是否发生了错误
			if (ifs.good() == false)
			{
				Log::log(LOGERROR, "FileUtil::GetPosLen::ifs.good()-get file content failed !: %s: %d", strerror(errno), errno);
				ifs.close();
				return false;
			}
			ifs.close();
			return true;
		}

		// 获取整个文件内容传递给content # 代码复用
		bool GetContent(std::string *content)
		{
			size_t fsize = this->FileSize();
			return GetPosLen(content, 0, fsize);
		}

		// 将content中的数据写入到_filePath指向的文件(不存在时 open会尝试创建)
		bool SetContent(const std::string &content)
		{
			std::ofstream ofs;
			ofs.open(_filePath, std::ios::binary);
			if (ofs.is_open() == false)
			{
				Log::log(LOGERROR, "FileUtil::SetContent::ofs.is_open()-open file failed !: %s: %d", strerror(errno), errno);
				return false;
			}
			// write接收const char*类型 不用像getPosLen一样做类型转换
			ofs.write(&content[0], content.size());
			if (ofs.good() == false)
			{
				Log::log(LOGERROR, "FileUtil::SetContent::ofs.good()-write file content failed !: %s: %d", strerror(errno), errno);
				ofs.close();
				return false;
			}
			ofs.close();
			return true;
		}

		// 将成员属性_filename中的数据压缩后存入zipFile
		bool Compress(const std::string &zipFile)
		{
			// 1. 获取原文件内容存入到content; binary->string
			std::string content;
			if (this->GetContent(&content) == false)
			{
				Log::log(LOGERROR, "FileUtil::Compress::GetContent()-get file content failed !: %s: %d", strerror(errno), errno);
				return false;
			}
			// 2. 对数据进行压缩 string->string.zip
			std::string packed = bundle::pack(bundle::LZIP, content);
			// 3. 将压缩后的数据存储到压缩包文件中
			FileUtil fileUtil(zipFile);
			if (fileUtil.SetContent(packed) == false)
			{
				Log::log(LOGERROR, "FileUtil::Compress::SetContent()-write packed data failed !: %s: %d", strerror(errno), errno);
				return false;
			}
			return true;
		}

		// 将成员属性_filename中的数据解压后存入unzipFile
		bool DeCompress(const std::string &unzipFile)
		{
			// 读取成员属性_filename压缩包数据存入content
			std::string content;
			if (this->GetContent(&content) == false)
			{
				Log::log(LOGERROR, "FileUtil::DeCompress::GetContent()-get file content failed !: %s: %d", strerror(errno), errno);
				return false;
			}
			// 对压缩包数据进行解压缩
			std::string unpacked = bundle::unpack(content);
			// 将解压缩的数据写入到新文件unzipFile
			FileUtil fileUtil(unzipFile);
			if (fileUtil.SetContent(unpacked) == false)
			{
				Log::log(LOGERROR, "FileUtil::DeCompress::SetContent()-write unpacked data failed !: %s: %d", strerror(errno), errno);
				return false;
			}
			return true;
		}

		// 判断文件是否存在
		bool Exists(filesystem::file_status status = filesystem::file_status{})
		{
			// bool exists(const path& p);
			return filesystem::exists(_filePath);
		}

		// 删除某个文件
		bool Remove()
		{
			if (this->Exists() == false)
				return true;

			// int remove(const char *pathname);
			remove(_filePath.c_str());
			return true;
		}

		// 创建目录 目录是一个可以包含文件的文件 路径中的任何父目录不存在 一并创建
		bool CreateDirectory()
		{
			// 已经存在 默认创建成功 直接返回
			if (this->Exists())
			{
				Log::log(INFO, "FileUtil::CreateDirectory::%s already exists!", _filePath.c_str());
				return true;
			}
			// 路径中的任何父目录不存在 一并创建
			return filesystem::create_directories(_filePath);
		}

		// 浏览_filePath这个 目录 下所有文件名 存入fileArray
		bool ScanDirectory(std::vector<std::string> *fileArray)
		{
			// 浏览前确保_filePath是一个目录而非普通文件
			if (filesystem::is_directory(_filePath))
			{
				// const fs::directory_entry &entry
				for (const auto &entry : filesystem::directory_iterator(_filePath))
				{
					// 如果浏览到了目录 则跳过 (我们只需要获取文件)
					if (filesystem::is_directory(entry) == true)
						continue;

					// relative_path 不带根路径的路径
					// C:\Users\abcdef\temp -> Users\abcdef\temp
					fileArray->push_back(filesystem::path(entry).relative_path().string());
				}
				return true;
			}
			Log::log(INFO, "FileUtil::CreateDirectory 接收了一个非目录的文件！: %s: %d", strerror(errno), errno);
			return false;
		}
		
		// URL 编码函数
		static std::string UrlEncode(const std::string &str)
		{
			std::ostringstream escaped;
			escaped.fill('0');
			escaped << std::hex;

			for (size_t i = 0; i < str.length(); ++i)
			{
				char c = str[i];
				// 处理不可打印的字符
				if (std::isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~')
				{
					escaped << c;
				}
				else
				{
					escaped << '%' << std::uppercase << std::setw(2) << int((unsigned char)c);
				}
			}
			return escaped.str();
		}

		static std::string UrlDecode(const std::string &encoded)
		{
			std::string decoded;
			size_t length = encoded.length();

			for (size_t i = 0; i < length; ++i)
			{
				if (encoded[i] == '%')
				{
					// 如果是%符号，则接下来的两个字符是十六进制值
					if (i + 2 < length && isxdigit(encoded[i + 1]) && isxdigit(encoded[i + 2]))
					{
						// 转换%后面的两位十六进制字符
						std::string hexStr = encoded.substr(i + 1, 2);
						char decodedChar = static_cast<char>(std::stoi(hexStr, nullptr, 16));
						decoded += decodedChar;
						i += 2; // 跳过已处理的两个字符
					}
					else
					{
						// 如果没有有效的十六进制字符，直接加%符号
						decoded += encoded[i];
					}
				}
				else if (encoded[i] == '+')
				{
					// "+"替代空格
					decoded += ' ';
				}
				else
				{
					// 其他字符直接添加到解码结果中
					decoded += encoded[i];
				}
			}

			return decoded;
		}
	};
}

#endif //__MY_FILEUTIL__
